﻿using System;
using System.Collections;


namespace OptimalisationFramework.Utilities.Graphs
{
    class Graph<Identification, Data, Weight> : IEnumerable
    {
        private Hashtable vertexList;
        
        public Graph()
        {
            vertexList = new Hashtable();           
        }


        /*
         * Additional tools
         * */
        public int VertexSize
        {
            get { return vertexList.Count; }
        }

        public int EdgeSize
        {
            get
            {
                int size = 0;
                foreach (Vertex<Identification, Data, Weight> v in vertexList)
                {
                    size += v.EdgeSize;
                }
                return size;
            }
        }

        /*
         * Vertex
         * */
        public void AddVertex(Identification id, Data d)
        {
            if (!VertexExists(id))
            {
                vertexList.Add(id, new Vertex<Identification, Data, Weight>(id, d));
            }
        }

        public void AddVertex(Vertex<Identification, Data, Weight> v)
        {
            if (!VertexExists(v.ID))
            {
                vertexList.Add(v.ID, v);
            }
        }

        public void RemoveVertex(Identification id)
        {
            if (VertexExists(id))
            {
                vertexList.Remove(id);
                foreach (Vertex<Identification, Data, Weight> v in vertexList)
                {
                    v.RemoveNeighbour(id);
                }
            }
        }

        public bool VertexExists(Identification id)
        {
            return vertexList.ContainsKey(id);
        }

        public Vertex<Identification, Data, Weight> GetVertex(Identification id)
        {
            if (VertexExists(id))
            {
                return (Vertex<Identification, Data, Weight>)vertexList[id];
            }            
            return null;
        }


        /*
         * Edges
         * */
        public void AddEdge(Identification begin, Identification end, Weight w)
        {
            if (VertexExists(begin) && VertexExists(end))
            {
                Vertex<Identification, Data,Weight> v = GetVertex(begin);
                v.AddNeighbour(end, w);
            }
        }


        public void RemoveEdge(Identification begin, Identification end)
        {
            if (VertexExists(begin) && VertexExists(end))
            {
                Vertex<Identification, Data, Weight> v = (Vertex<Identification, Data, Weight>)vertexList[begin];
                v.RemoveNeighbour(end);
            }
        }


        /*
         * Output
         * */
        public void WriteGraph()
        {
            foreach (Vertex<Identification, Data, Weight> v in this)
            {
                Console.WriteLine("ID:\t" + v.ID);
                foreach (Edge<Identification, Data, Weight> e in v)
                {
                    Console.WriteLine("\t\t-> " + e.End + " (" + e.W + ")");
                }
            }
           
        }


        public void ReplaceVertex(Vertex<Identification, Data, Weight> v)
        {
            if (vertexList.Contains(v.ID))
            {
                vertexList.Remove(v.ID);
            }
            vertexList.Add(v.ID, v);
        }

        /*
         * Enumerators
         * */
        public IEnumerator GetEnumerator()
        {
            return new GraphEnumerator<Identification, Data, Weight>(this);            
        }

        internal Hashtable Vertexes
        {
            get
            {
                return (vertexList);
            }
        }

        /*
         * Indexer
         * */
        public Vertex<Identification, Data, Weight> this [int index]
        {
            get
            {
                ICollection keys = vertexList.Values;
                IEnumerator ie= keys.GetEnumerator();
                ie.Reset();
                int i = 0;
                while (ie.MoveNext())
                {
                    if (i == index)
                    {
                        return (Vertex<Identification, Data, Weight>)ie.Current;
                    }
                    i++;
                }
                return null;
            }
            /*
            set
            {
                data[index] = value;
            }
             */
        }

    }


    class GraphEnumerator<Identification, Data, Weight> : IEnumerator
    {
        private Graph<Identification, Data, Weight> g;
        private IEnumerator ie;

        internal GraphEnumerator(Graph<Identification, Data, Weight> g)
        {
            this.g = g;
            ie = g.Vertexes.GetEnumerator();
            ie.Reset();
        }

        public bool MoveNext()
        {
            return ie.MoveNext();
        }

        public object Current
        {
            get
            {
                DictionaryEntry di = (DictionaryEntry)ie.Current;
                return di.Value;
            }
        }

        public void Reset()
        {
            ie.Reset();
        }
    }
}
